package thread;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @User: vitobo
 * @Date: 2024-08-02
 * @Description: 定时器实现
 *
 * 定时器实现:
 *  1.让被注册的任务, 能够在指定时间,被执行
 *      单独在定时器内部搞一个 扫描线程, 让这个线程周期性的扫描,判定任务是否到时间了; 时间到了就执行,
 *  2.一个定时器是可以注册N个任务的, N个任务会按照最初约定的时间, 按顺序执行;
 *      N个任务使用 数据结构(优先级队列) 来保存;
 *          按时间顺序,放到以时间作为优先级的优先级队列中;
 *          此处的优先级队列会在多线程 环境下使用;<需要考虑线程安全问题>
 *          还可以选择 标准库中提供的 PriorityBlockQueue
 */

// 表示定时器中的任务
class MyTask implements Comparable<MyTask>{
    // 要执行的任务内容
    private Runnable runnable;

    // 任务在什么时候执行
    private long time;

    public MyTask(Runnable runnable, long time){
        this.runnable = runnable;
        this.time = time;
    }

    public long getTime() {
        return time;
    }

    public void run(){
        runnable.run();
    }

    @Override
    public int compareTo(MyTask o){
        // this 比 o 小, 返回 < 0
        // this 比 o 大, 返回 > 0
        // this 和 o 相同, 返回 0
        return (int)(this.time - o.time);
    }
}

// 模拟实现定时器
class MyTimer{
    // 扫描线程
    private Thread t = null;

    // 阻塞优先级队列, 来保存任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public MyTimer(){
        t = new Thread(()->{
           while (true){
               try{
                   // 把锁的范围放大, 放大之后, 此时就可以保证, 执行notify的时候,wait已经是执行完了,就可以预防出现notify 的时候还没有准备好wait这样的情况了;
                   synchronized (this){
                       // 取出队首元素, 检查队首元素是否到时间了
                       // 如果时间没到,就把任务塞回队列里去
                       // 如果时间到了,就执行任务
                       MyTask myTask = queue.take();
                       long currentTime = System.currentTimeMillis();
                       if(currentTime < myTask.getTime()){
                           // 还没到点, 先不执行, 塞回优先级队列
                           queue.put(myTask);

                           // 在put之后,进行一个wait
                           this.wait(myTask.getTime() - currentTime);
                       }else {
                           myTask.run();
                       }
                   }
               }catch (InterruptedException e){
                   e.printStackTrace();
               }
           }
        });
        t.start();
    }

    /**
     * 指定两个参数
     * @param runnable: 任务内容
     * @param after: 任务在 多少毫秒后 执行
     */
    public void schedule(Runnable runnable, long after){
        // 注意时间换算
        MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);
        queue.put(task);

        // 加上通知操作
        synchronized (this){
            this.notify();
        }
    }

}


public class ThreadDemo25 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务1");
            }
        }, 1000);

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务2");
            }
        }, 2000);
    }
}
